package dkbta.ontology.loading;

import static dkbta.ontology.loading.OntologyParser.parseNumericValues;
import static dkbta.util.Utils.enumForName;

import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Set;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import dkbta.ontology.Ontology;
import dkbta.ontology.elements.ElementType;
import dkbta.ontology.elements.TypedName;
import dkbta.ontology.elements.abstractions.TemporalSemanticProperty;
import dkbta.ontology.elements.abstractions.interpolation.PersistenceFunction;
import dkbta.ontology.elements.abstractions.rate.RateDef;
import dkbta.ontology.elements.abstractions.rate.RateMappingFunction;
import dkbta.ontology.values.NumericValues;
import dkbta.ontology.values.ScaledSymbolicValues;
import dkbta.ontology.values.SymbolicValues;
import dkbta.util.time.TimeUnit;

public class RateParser extends AbstractionParser{

	public RateParser(Ontology ontology){
		super(ontology, EnumSet.complementOf(EnumSet.of(ElementType.PRIMITIVE)));
	}

	@Override
	public ElementType getElementType(){
		return ElementType.RATE;
	}

	@Override
	protected void parseAbstraction(Element rate, String name, String componentType,
		Set<TypedName> abstractedFrom, Set<String> necessaryContexts,
		Set<TemporalSemanticProperty> tsps, ScaledSymbolicValues scaledValues,
		SymbolicValues values, PersistenceFunction persistFunc) throws OntologyException{
		if (abstractedFrom.size() != 1){
			throw new OntologyException(
					"A rate can be abstracted from only one primitive");
		}
		String afPrimitive = abstractedFrom.iterator().next().getName();

		RateMappingFunction mapFunc = parseMappingFunction(rate, values, abstractedFrom);
		if (mapFunc == null){
			throw new OntologyException("Rate without a mapping function");
		}
		_ontology.addRate(new RateDef(name, componentType, scaledValues, afPrimitive,
				necessaryContexts, tsps, mapFunc, persistFunc));
	}

	private RateMappingFunction parseMappingFunction(Element gradient,
		SymbolicValues values, Set<TypedName> abstractedFrom) throws OntologyException{
		Element mappingFuncElement = OntologyParser.getXmlElement(gradient,
			"MappingFunction");
		if (mappingFuncElement == null){
			return null;
		}
		TimeUnit timeUnit = enumForName(TimeUnit.class, mappingFuncElement
				.getAttribute("timeUnit"));

		// Obtaining the number of entries
		NodeList valueNodes = mappingFuncElement.getChildNodes();
		Collection<RateMappingFunction.Entry> entries = new ArrayList<RateMappingFunction.Entry>(
				valueNodes.getLength());

		for (int i = 0; i < valueNodes.getLength(); ++i){
			Node node = valueNodes.item(i);
			if (node.getNodeType() != Node.ELEMENT_NODE){
				continue;
			}
			Element valueElement = (Element)node;
			// Parsing the return/abstracted value for the entry
			String value = valueElement.getAttribute("name");
			String mappedValue = values.parseAndValidate(value);
			if (mappedValue == null){
				throw new OntologyException("Invalid/missing value '" + value
						+ "' in the mapping function");
			}

			// Parsing the valid values for the mapped value and converting them to
			// milliseconds in the process
			NumericValues validValues = parseNumericValues(valueElement, false, timeUnit);
			entries.add(new RateMappingFunction.Entry(mappedValue, validValues));
		}

		return new RateMappingFunction(entries);
	}
}
